# coding=utf-8
import datetime
import os
import sys
import struct
import time
import json
import logging
import urllib2
from socket import *
from struct import pack
from threading import Thread

reload(sys)
sys.setdefaultencoding('utf-8')


logger = logging.getLogger()
logger.setLevel(logging.INFO)
sh = logging.StreamHandler(stream=sys.stdout)  # output to standard output
sh.setFormatter(logging.Formatter("%(asctime)s - %(message)s"))
logger.addHandler(sh)

notify_item_cache = {}
scan_items = []
item_interval = 1.0
cache_details = {}

server_host = "139.9.55.19"
sync_url = "http://" + server_host + ":8080/douya/purchaser/syncTask.do"
#sync_url = "http://127.0.0.1:8080/douya/purchaser/syncTask.do"
last_sync_time = 0
##producer = KafkaProducer(bootstrap_servers='139.9.55.19:9092')
scan_count = 0
find_count = 0
server_id = "test111"
version = "v1.8"
clients = {}

def get_ext_ip():
    try:
        url = "http://" + server_host + ":8080/douya/ip/getIpInfo.do"
        req = urllib2.Request(url)
        response = urllib2.urlopen(req)
        res = response.read()
        res = json.loads(res)
        if res["ret"] == 0:
            return res["ip"]
    except Exception, e:
        logger.error(e)
        return ""


def is_available():
    try:
        url = "https://buff.163.com/api/market/goods/sell_order"
        req = urllib2.Request(url)
        response = urllib2.urlopen(req)
        res = response.read()
        res = json.loads(res)
        if res["code"] != "":
            return True
    except Exception, e:
        logger.error(e)
        return False


def sell_order(scan_item):
    try:
        url_base = "https://buff.163.com/api/market/goods/sell_order?game={0}&goods_id=" \
                   "{1}&page_num=1&sort_by=default&mode=&allow_tradable_cooldown=1&_={2}"
        url = url_base.format(scan_item["game"], scan_item["cateId"], int(round(time.time() * 1000)))
        logger.info("request url= " + url)
        req = urllib2.Request(url)
        response = urllib2.urlopen(req)
        res = response.read()
        res = json.loads(res)
        if res["code"] == "OK":
            logger.info("ok, item size: " + str(len(res["data"]["items"])))
            items = res["data"]["items"]
            fill_detail(items, scan_item["name"])
            return items
        else:
            logger.info(res)
    except Exception, e:
        logger.error(e)


def fill_detail(items, name):
    global cache_details
    if len(cache_details) > 100:
        cache_details = {}
    for item in items:
        id = item["id"]
        if id in cache_details:
            item["detail"] = cache_details[id]
        else:
            detail = item_detail(item, name)
            if detail == "":
                logger.error("can not get detail from item id: {0}, name: {1}".format(id, name))
                continue
            cache_details[id] = detail
            item["detail"] = detail


def item_detail(item, name):
    asset_info = item["asset_info"]
    appid = item["appid"]
    game = item["game"]
    sell_order_id = item["id"]

    classid = asset_info["classid"]
    instanceid = asset_info["instanceid"]
    assetid = asset_info["assetid"]
    contextid = asset_info["assetid"]

    try:
        url_base = "https://buff.163.com/market/item_detail?appid={0}&game={1}&classid={2}&instanceid=" \
                   "{3}&sell_order_id={4}&origin=selling-list&assetid={5}&contextid={6}&_={7} "
        url = url_base.format(appid, game, classid, instanceid, sell_order_id, assetid, contextid, int(round(time.time() * 1000)))
        logger.info("request url= " + url)
        req = urllib2.Request(url)
        response = urllib2.urlopen(req)
        res = response.read()
        name = name.encode('utf-8')
        if res.index(name) > -1:
            return res

    except Exception, e:
        logger.error(e)
    return ""


def need_buy(sell_item, scan_item):
    threshold_price = float(scan_item["price"])
    sell_price = float(sell_item["price"])

    if sell_price > threshold_price:
        return False
    detail = sell_item["detail"]
    properties = scan_item["property"].encode('utf-8')
    properties = properties.split('$')
    match_count = 0
    for pro in properties:
        if not pro.startswith('!') and detail.find(pro) > -1:
            return True
        elif pro.startswith('!') and detail.find(pro[1:]) == -1:
            match_count += 1

    if match_count == len(properties):
        return True
    return False


def need_notify(item):
    global notify_item_cache
    if item["id"] in notify_item_cache:
        logger.info("{0} already notify".format(item["id"]))
        return False
    if len(notify_item_cache) > 1000:
        notify_item_cache = {}
    notify_item_cache[item["id"]] = 1
    return True


def notify(item):
    logger.info("notify1 need by item: {0} ignore".format(item["id"]))
    # detail = item["detail"]
    # global producer
    # item["server_id"] = server_id
    # item["detail"] = None
    # msg = json.dumps(item)
    # producer.send('purchaser', msg, key="buff", partition=0)
    # item["detail"] = detail


def notify2(item):
    logger.info("notify2 need by item: {0}".format(item["id"]))
    detail = item["detail"]
    item["server_id"] = server_id
    item["detail"] = None
    msg = json.dumps(item)
    global clients
    for fd in clients:
        try:
            client = clients[fd]
            sock = client["socket"]
            info = client["info"]
            val = pack('H', len(msg))
            sock.sendall(val)
            sock.sendall(msg)
            logger.info("send msg: %s to client: %s:%d", msg, info[0], info[1])
        except Exception, e:
            logger.error(e)
    item["detail"] = detail


def send_tcp_msg(client, msg):
    sock = client["socket"]
    info = client["info"]
    val = pack('H', len(msg))
    sock.sendall(val)
    sock.sendall(msg)
    logger.info("send msg: %s to client: %s:%d", msg, info[0], info[1])


def sync_task():
    global last_sync_time
    global scan_items
    global sync_url
    global server_id
    global scan_count
    global find_count
    global item_interval

    logger.info("sync task {0}".format(sync_url))
    try:
        data = {
            "serverId": server_id,
            "scanCount": scan_count,
            "findCount": find_count,
            "workerVersion": version,
            "itemReporters": []
        }
        for item in scan_items:
            data["itemReporters"].append({
                "scanCount": item["scanCount"],
                "findCount": item["findCount"],
                "itemId": item["id"],
            })

        logger.info("request url= " + sync_url + " data = " + json.dumps(data))
        headers = {'Content-Type': 'application/json', 'Accept': 'application/json, text/javascript, */*; q=0.01'}
        req = urllib2.Request(sync_url, headers=headers, data=json.dumps(data))
        response = urllib2.urlopen(req)
        res = response.read()
        logger.info(res)
        response = json.loads(res)
        if response["code"] == 0:
            items = response["data"]
            worker = response["worker"]
            for item in items:
                item["scanCount"] = 0
                item["findCount"] = 0
            scan_items = items
            if worker:
                item_interval = worker["itemInterval"]
        scan_count = 0
        find_count = 0
    except Exception, e:
        logger.error(e)


def sync_task_if_need():
    global last_sync_time
    now = datetime.datetime.now()
    if last_sync_time != 0 and (now - last_sync_time).total_seconds() < 15:
        return
    last_sync_time = now
    sync_task()


def run_one():
    global scan_items
    global scan_count
    global find_count
    sync_task_if_need()
    for scan_item in scan_items:
        try:
            sell_items = sell_order(scan_item)
            for sell_item in sell_items:
                if need_buy(sell_item, scan_item):
                    if not need_notify(sell_item):
                        continue
                    scan_item["findCount"] = scan_item["findCount"] + 1
                    find_count = find_count + 1
                    sell_item["scan_id"] = scan_item["id"]
                    sell_item["scan_name"] = scan_item["name"]
                    logger.info("need buy item: %s", sell_item["scan_name"])
                    notify2(sell_item)
                    notify(sell_item)
            scan_item["scanCount"] = scan_item["scanCount"] + 1
            scan_count = scan_count + 1
            time.sleep(item_interval)
        except Exception, e:
            logger.error(e)


def client_thread(client_socket, client_info):
    global clients
    client_socket.settimeout(120)
    clients[client_socket.fileno()] = {
        "socket": client_socket,
        "info": client_info
    }
    print(client_info[0])
    logger.info("accpet %s:%d, key: %d ", client_info[0], client_info[1], client_socket.fileno())
    while True:
        try:
            data = client_socket.recv(2)
            if not data:
                logger.info("client disconnect %s:%d", client_info[0], client_info[1])
                break
            len = struct.unpack("H", data)[0]
            logger.info("recv data form, %s:%d, len: %d ", client_info[0], client_info[1], len)
            data = client_socket.recv(len)
            if not data:
                logger.info("client disconnect: %s:%d", client_info[0], client_info[1])
                break
            logger.info("recv data form %s:%d, data: %s ", client_info[0], client_info[1], data)
            send_tcp_msg({
                "socket": client_socket,
                "info": client_info
            }, data)
        except Exception, e:
            logger.error(e)
            break

    logger.info("disconnect: %s:%d", client_info[0], client_info[1])
    del clients[client_socket.fileno()]
    dump_clients()


def dump_clients():
    global clients
    msg = ""
    for fd in clients:
        client = clients[fd]
        msg += client["info"][0] + ":" + str(client["info"][1]) + '\n'

    logger.info("current clients: %s", msg)


def accept(server_socket):
    while True:
        try:
            client_socket, client_info = server_socket.accept()
            p = Thread(target=client_thread, args=(client_socket, client_info, ))
            p.start()
        except Exception, e:
            logger.info(e)
            exit(-1)


def tcp_server():
    try:
        server_socket = socket(AF_INET, SOCK_STREAM)
        server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        server_socket.bind(("", 8899))
        server_socket.listen(5)
        Thread(target=accept, args=(server_socket, )).start()
    except Exception, e:
        logger.info(e)
        server_socket.close()
        exit(-1)


if not is_available():
    logger.error("server not available")
    exit(-1)
logger.info("server is available")

server_id = get_ext_ip()
#server_id = "192.168.23.130"
tcp_server()
logger.info("set service id: %s", server_id)
if server_id == "":
    logger.error("can't get ext ip")
    exit(-1)

while True:
    try:
        run_one()
    except Exception, e:
        logger.error(e)
